﻿using System;
using System.ComponentModel;
using Ops.Mpc.ValidationServices;
using System.Text;
using Ops.Mpc.Collections;
using System.Collections.Generic;
using Ops.Mpc.Controllers;
using System.Collections.ObjectModel;
using System.Collections;
using System.Linq.Expressions;

namespace Ops.Mpc.Models
{
 
    /// <summary>
    /// Base class for custom classes used to manage application data.
    /// </summary>
    public class Model :  INotifyPropertyChanged, IDisposable, INamedItem
#if !WINDOWS_PHONE
        , INotifyDataErrorInfo 
#endif
    {
        #region Fields & Properties
        private readonly Dictionary<string, List<ValidationResult>> _validationResults = new Dictionary<string, List<ValidationResult>>();
        private readonly ObservableCollection<IVisualStateMap> _visualStateMapCollection = new ObservableCollection<IVisualStateMap>();
        public event PropertyChangedEventHandler PropertyChanged;
        private string _itemName = string.Empty;
        private bool _isValid = false;
        private IController _controller = null;


        /// <summary>
        /// Results from validation
        /// </summary>
        public Dictionary<string, List<ValidationResult>> ValidationResults
        {
            get
            {
                return this._validationResults;
            }
        }

        /// <summary>
        /// Is the data within the model valid
        /// </summary>
        public bool IsValid
        {
            get
            {
                return this._isValid;
            }

            private set
            {
                if (this._isValid != value)
                {
                    this._isValid = value;
                    this.RaisePropertyChanged("IsValid");
                }

            }
        }

        /// <summary>
        /// The item name of the model
        /// </summary>
        public string ItemName
        {
            get { return this._itemName; }
            set { this._itemName = value; }
        }

        public ObservableCollection<IVisualStateMap> VisualStateMaps
        {
            get { return this._visualStateMapCollection; }
        }

        protected IController Controller
        {
            get
            {
                return this._controller;
            }
        }
        #endregion 

        #region Constructors

        /// <summary>
        /// Default constructor
        /// </summary>
        public Model()
        {
            //this.Validate();
        }


        #endregion 

        #region Private & Protected Methods


        protected void RaisePropertyChanged(string propertyName)
        {
            this.RaisePropertyChanged(propertyName, true);
        }

        protected void RaisePropertyChanged(string propertyName, bool validate)
        {
            if (!this.Controller.Presenter.Dispatcher.CheckAccess())
                this.Controller.Presenter.Dispatcher.BeginInvoke(() => { this.RaisePropertyChanged(propertyName, validate); });
            else
            {
                try
                {
                    ValidationResult[] validationResults = ValidationManager.Validate(this, propertyName);

                    if (validationResults.Length > 0)
                    {
                        StringBuilder stringBuilder = new StringBuilder();

                        this._validationResults[propertyName] = new List<ValidationResult>(validationResults);

                        this.IsValid = false;

                        foreach (ValidationResult validationResult in validationResults)
                            stringBuilder.AppendLine(validationResult.Reason);

                    }
                    else if (this._validationResults.ContainsKey(propertyName))
                    {
                        this._validationResults.Remove(propertyName);
                        this.IsValid = this._validationResults.Count < 1;


                    }
                }
                finally
                {
                    if (this.PropertyChanged != null)
                        this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
#if !WINDOWS_PHONE
                    if (this.ErrorsChanged != null)
                        this.ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
#endif

                    this.OnInvokeVisualStateMapForPropertyChanged(propertyName);

                }
            }
        }

        protected virtual void OnInvokeVisualStateMapForPropertyChanged(string propertyName)
        {
            if (this._controller != null)
            {
                if (!this._controller.Presenter.Dispatcher.CheckAccess())
                {
                    this._controller.Presenter.Dispatcher.BeginInvoke(delegate { OnInvokeVisualStateMapForPropertyChanged(propertyName); });
                    return;
                }


                foreach (IVisualStateMap iVisualStateMap in this._visualStateMapCollection)
                    if (iVisualStateMap.Evaluate(this._controller,this, propertyName))
                        iVisualStateMap.Execute(this._controller);
            }
            
        }


        #endregion 

        #region Public Methods
        public virtual void Start()
        {

        }

        public virtual void Stop()
        {

        }

        internal void Start(IController controller)
        {
            this._controller = controller;
            this.Start();
        }

        public void Validate()
        {
            this._validationResults.Clear();

            foreach(ValidationResult validationResult in ValidationManager.Validate(this))
            {
                if(!this._validationResults.ContainsKey(validationResult.Property))
                    this._validationResults.Add(validationResult.Property,new List<ValidationResult>());

                this._validationResults[validationResult.Property].Add(validationResult);

            }

            this.IsValid = this._validationResults.Count < 1;
            
#if !WINDOWS_PHONE
            if (this.ErrorsChanged != null)
                foreach (string key in this._validationResults.Keys)
                {
                    this.ErrorsChanged(this, new DataErrorsChangedEventArgs(key));
                }
#endif

        }
        #endregion 

        #region IDisposable Members

        public virtual void Dispose()
        {
           
        }

        #endregion

        #region INotifyDataErrorInfo Members
#if !WINDOWS_PHONE
        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

        IEnumerable INotifyDataErrorInfo.GetErrors(string propertyName)
        {
            List<string> errorList = new List<string>();

            if (this._validationResults.ContainsKey(propertyName))
            {
                foreach (ValidationResult validationResult in this.ValidationResults[propertyName])
                    errorList.Add(validationResult.Reason);

            }

            return errorList.ToArray();
        }

        bool INotifyDataErrorInfo.HasErrors
        {
            get { return !this.IsValid; }
        }
#endif
        #endregion
    }

    public class Model<ModelType> : Model
        where ModelType : class
    {
        protected virtual void RaisePropertyChanged<ResultType>(Expression<Func<ModelType, ResultType>> property)
        {
            string propertyName = ((MemberExpression)property.Body).Member.Name;

            this.RaisePropertyChanged(propertyName);
        }

        protected virtual void RaisePropertyChanged<Type,ResultType>(Expression<Func<Type, ResultType>> property)
        {
            string propertyName = ((MemberExpression)property.Body).Member.Name;

            this.RaisePropertyChanged(propertyName);
        }
    }
}
